package com.gitee.wsl.func.filter.bloomfilter.api

import com.gitee.wsl.ext.base.Arrays.copyOf
import com.gitee.wsl.ext.base.Arrays.ensureCapacityForAdd
import com.gitee.wsl.func.predicate.IntPredicate
import com.gitee.wsl.func.predicate.LongPredicate
import com.gitee.wsl.lang.bits.BitSet


/**
 * An object that produces indices of a Bloom filter.
 *
 * *
 * The default implementation of `asIndexArray` is slow. Implementers should reimplement the
 * method where possible.*
 *
 * @since 4.5.0-M2
 */
fun interface IndexExtractor {
    /**
     * Return a copy of the IndexExtractor data as an int array.
     *
     *
     * Indices ordering and uniqueness is not guaranteed.
     *
     *
     * *
     * The default implementation of this method creates an array and populates
     * it.  Implementations that have access to an index array should consider
     * returning a copy of that array if possible.
     * *
     *
     * @return An int array of the data.
     */
    fun asIndexArray(): IntArray {
        class Indices {
            private var data = IntArray(32)
            private var size = 0

            fun add(index: Int): Boolean {
                data = ensureCapacityForAdd(data, size)
                data[size++] = index
                return true
            }

            fun toArray(): IntArray {
                // Edge case to avoid a large array copy
                return if (size == data.size) data else copyOf(data, size)
            }
        }

        val indices = Indices()
        processIndices(IntPredicate { index: Int -> indices.add(index) })
        return indices.toArray()
    }

    /**
     * Each index is passed to the predicate. The predicate is applied to each
     * index value, if the predicate returns `false` the execution is stopped, `false`
     * is returned, and no further indices are processed.
     *
     *
     * Any exceptions thrown by the action are relayed to the caller.
     *
     *
     * Indices ordering and uniqueness is not guaranteed.
     *
     * @param predicate the action to be performed for each non-zero bit index.
     * @return `true` if all indexes return true from consumer, `false` otherwise.
     * @throws NullPointerException if the specified action is null
     */
    fun processIndices(predicate: IntPredicate): Boolean

    /**
     * Creates an IndexExtractor comprising the unique indices for this extractor.
     *
     *
     * By default creates a new extractor with some overhead to remove
     * duplicates.  IndexExtractors that return unique indices by default
     * should override this to return `this`.
     *
     *
     * The default implementation will filter the indices from this instance
     * and return them in ascending order.
     *
     * @return the IndexExtractor of unique values.
     * @throws IndexOutOfBoundsException if any index is less than zero.
     */
    fun uniqueIndices(): IndexExtractor {
        val bitSet = BitSet()
        processIndices(IntPredicate { i ->
            bitSet.set(i)
            true
        })

        return object : IndexExtractor {
            override fun processIndices(predicate: IntPredicate): Boolean {
                var idx = bitSet.nextSetBit(0)
                while (idx >= 0) {
                    if (!predicate.test(idx)) {
                        return false
                    }
                    idx = bitSet.nextSetBit(idx + 1)
                }
                return true
            }

            override fun uniqueIndices(): IndexExtractor {
                return this
            }
        }
    }

    companion object {


        /**
         * Creates an IndexExtractor from a `BitMapExtractor`.
         *
         * @param bitMapExtractor the `BitMapExtractor`
         * @return a new `IndexExtractor`.
         */
        fun fromBitMapExtractor(bitMapExtractor: BitMapExtractor): IndexExtractor {

            return IndexExtractor { consumer: IntPredicate ->
                val longPredicate: LongPredicate = object : LongPredicate {
                    var wordIdx: Int = 0

                    override fun test(t: Long): Boolean {
                        var word = t
                        var i = wordIdx
                        while (word != 0L) {
                            if ((word and 1L) == 1L && !consumer.test(i)) {
                                return false
                            }
                            word = word ushr 1
                            i++
                        }
                        wordIdx += 64
                        return true
                    }
                }
                bitMapExtractor.processBitMaps(longPredicate::test)
            }
        }


//        fun fromIndexArray(values: IntArray): IndexExtractor = object : IndexExtractor {
//            override fun asIndexArray(): IntArray {
//                return values
//            }
//
//            override fun processIndices(predicate: IntPredicate): Boolean {
//                for (value in values) {
//                    if (!predicate.test(value)) {
//                        return false
//                    }
//                }
//                return true
//            }
//        }

        /**
         * Creates an IndexExtractor from an array of integers.
         *
         * @param values the index values
         * @return an IndexExtractor that uses the values.
         */
        fun fromIndexArray(vararg values: Int): IndexExtractor {
            return object : IndexExtractor {
                override fun asIndexArray(): IntArray {
                    return values.clone()
                }

                override fun processIndices(predicate: IntPredicate): Boolean {
                    for (value in values) {
                        if (!predicate.test(value)) {
                            return false
                        }
                    }
                    return true
                }
            }
        }
    }
}
